/**
 * Copyright (c) 2012 Fujian FUNO Mobile Communication Technology Co., Ltd.
 * All rights reserved by FUNO.
 */
package com.funo.ehealth.DB;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.funo.ehealth.util.LogUtil;

/**
 * dbcp数据库连接池<br>
 * 由于原先使用ConnectionPool连接池进行数据库连接的管理,在不改变太多已有代码的情况下,提供和ConnectionPool一样的接口,
 * 但内部使用dbcp实现.<br>
 * 在第一次被load的时候进行数据库连接池的初始化,读取dbpool.properties配对文件.
 * 如果没有找到配置文件,数据库连接池无法初始化,就抛出一个 ExceptionInInitializerError结束初始化.
 * 
 * <hr/>
 * <b>@date 2012-9-26:</b> 新建
 * <hr/>
 * 
 * @author linzhiwei
 * @version 1.0
 */
public class DbcpConnectionPool {

	private static final Log log = LogFactory.getLog(DbcpConnectionPool.class);
	
	/**
	 * 以后如果配置事物,可将connection绑定到ThreadConnection上
	 */
	public static final ThreadLocal<Connection> ThreadLocalConnection = new ThreadLocal<Connection>();

	/**
	 * 数据库配置文件
	 */
	private static final String DB_CONFIG_FILE = "dbcp.properties";

	private static BasicDataSource dataSource = null;

	/**
	 * 初始化数据库连接池
	 */
	public static void init(){
		// 读取配置db文件,导入propertie里面
		InputStream inputStream = DbcpConnectionPool.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
		if (null == inputStream) {
			LogUtil.error(log, null, "classpath下没有找到配置文件{}", DB_CONFIG_FILE);
		}

		Properties properties = new Properties();
		try {
			properties.load(inputStream);
			LogUtil.info(log, "读取的配置文件的内容为:{}", properties.toString());
		} catch (IOException e) {
			LogUtil.error(log, e, "读取配置文件{}异常", DB_CONFIG_FILE);
		}

		dataSource = new BasicDataSource();
		dataSource.setDriverClassName(properties.getProperty("jdbc.driverClassName"));
		dataSource.setUrl(properties.getProperty("jdbc.url"));
		dataSource.setUsername(properties.getProperty("jdbc.username"));
		dataSource.setPassword(properties.getProperty("jdbc.password"));

		// 初始化连接数
		dataSource.setInitialSize(Integer.parseInt(properties.getProperty("initialSize")));

		// 连接池的最大活动个数
		dataSource.setMaxActive(Integer.valueOf(properties.getProperty("maxActive")));

		// 没有人用连接的时候，最大闲置的连接个数
		dataSource.setMaxIdle(Integer.valueOf(properties.getProperty("maxIdle")));

		// 没有人用连接的时候，最小闲置的连接个数
		dataSource.setMinIdle(Integer.valueOf(properties.getProperty("minIdle")));

		// 最大等待时间
		dataSource.setMaxWait(Integer.valueOf(properties.getProperty("maxWait")));

		// 默认自动提交事务
		dataSource.setDefaultAutoCommit(Boolean.valueOf(properties.getProperty("defaultAutoCommit")));

		// 是否自动回收超时连接
		dataSource.setRemoveAbandoned(Boolean.valueOf(properties.getProperty("removeAbandoned")));

		// 超时时间
		dataSource.setRemoveAbandonedTimeout(Integer.valueOf(properties.getProperty("removeAbandonedTimeout")));

		// 是否在自动回收超时连接的时候打印连接的超时错误
		dataSource.setLogAbandoned(Boolean.valueOf(properties.getProperty("logAbandoned")));

		// 是否开启池的prepared statement 池功能
		dataSource.setPoolPreparedStatements(Boolean.valueOf(properties.getProperty("poolPreparedStatements")));

		// 在取出连接时进行有效验证
		dataSource.setTestOnBorrow(Boolean.valueOf(properties.getProperty("testOnBorrow")));

		// 在返回连接时进行有效验证
		dataSource.setTestOnReturn(Boolean.valueOf(properties.getProperty("testOnReturn")));

		// 在空闲连接时进行有效验证
		dataSource.setTestWhileIdle(Boolean.valueOf(properties.getProperty("testWhileIdle")));

		// 连接空闲时间判断
		dataSource.setMinEvictableIdleTimeMillis(Integer.valueOf(properties.getProperty("minEvictableIdleTimeMillis")));
		
		// 检测空闲连接线程启动的的时间间隔
		dataSource.setTimeBetweenEvictionRunsMillis(Integer.valueOf(properties
				.getProperty("timeBetweenEvictionRunsMillis")));

		// sql语句进行验证
		dataSource.setValidationQuery(properties.getProperty("validationQuery"));
		
		//预先获取一个连接，以保证启动时能正常获取数据库连接
		getInstance().release(getInstance().getConnection());
	}

	private static final DbcpConnectionPool instance = new DbcpConnectionPool();
	
	/**
	 * 获取连接池实例
	 * 
	 * @return
	 */
	public static DbcpConnectionPool getInstance() {
		return instance;
	}

	private DbcpConnectionPool() {
	}

	/**
	 * 释放连接,将connection放回到pool中
	 * 
	 * @param conn
	 */
	public void release(Connection conn) {
		try {
			if(null != conn){
				conn.close();
			}
		} catch (SQLException e) {
			throw new RuntimeException("关闭statement异常",e);
//			log.error("关闭statement异常",e);
		}
	}

	/**
	 * 关闭连接池
	 */
	public void closePool() {
		try {
			if ( null!=dataSource && !dataSource.isClosed()) {
				dataSource.close();
			}
		} catch (SQLException e) {
			throw new RuntimeException("关闭连接池异常",e);
		}
	}

	/**
	 * 获取连接,先从ThreadConnection里面获取,有的话直接返回, 没有的话新建
	 * 
	 * @return
	 */
	public Connection getConnection() {
		try {
			Connection conn = null;
			if (null != (conn = ThreadLocalConnection.get())) {
				LogUtil.debug(log, "ThreadLocal存在conn,直接返回{}", conn);
				return conn;
			}
			conn = dataSource.getConnection();
			LogUtil.debug(log, "获取新连接{}", conn);
			return conn; 
		} catch (SQLException e) {
//			log.error("获取数据库连接发生错误", e);
			throw new RuntimeException("获取数据库连接发生错误", e);
		}
	}
	
	public void setConnection2CurrentThread(Connection connection){
		ThreadLocalConnection.set(connection);
	}

}
